package BLL;

import java.util.*;

import DTO.*;
import DAL.*;
import GUI.*;

/**
 * Classe responsável pelo gerenciamento de um jogo em execução.
 *
 * @author (R. Soares)
 * @version (1.0 - 24.05.2010)
 */
public class ExecutorJogo
{
    // Variáveis de Instancia ------------------------------------------ *
    private JogoDTO _jogo;
    private JogoGUI _jogoGUI;

    /**
     * Construtor da Classe ElementoJogo
     */
    public ExecutorJogo(JogoDTO jogo)
    {
        _jogo = jogo;
        _jogoGUI = new JogoGUI();
    }

    // Método ------------------------------------------------------- *

    /**
     * Método que retorna o atual jogo ativo.
     */
    public JogoDTO retornarJogoAtivo()
    {
        return _jogo;
    }

    /**
     * Método que realiza a execução de um jogo. set como parametro o tipo de ação a tomar
     * na execução do jogo (0 : Não atualizar tela;  1 : Atualizar tela). retorna o identificador do atual
     * jogo em execução.
     *
     * @param  int - Identificador da ação a ser realizada.
     * @return  String : Identificador do jogo em execução.
     */
    public String executarJogo(int acao)
    {
           TelaJogo tela = TelaJogo.retornaTela();

           // Verifica a cao tomada     0 : Novo Jogo, 1 : Renovar jogo,   qq num : Retomar Jogo (não testada)
           if (acao == 0)
                inicializarJogo();

           if(acao == 1)
           {
               tela.apagarTela();
               tela = TelaJogo.retornaTela();
               inicializarJogo();
           }

           while (tela.retornaBotaoEnter() != true && tela.retornaBotaoAcima() != true)
           {
               // Realiza movimentação de meteoros.
               moveMeteoros();

               // Realiza movimentação do satélite
               moveSatelite(tela.retornaBotaoDireita(), tela.retornaBotaoEsquerda() );

               // Realiza geração de mísseis
               gerarMisseis(tela.retornaBotaoEspaco(), tela.retornaBotaoAbaixo() );

               // Realiza a movimentação dos mísseis
               moveMisseis();

               // Detecta a colisão entre os elementos do jogo
               detectarColisoes();

               // Verificar e tratar evolução de jogo
               tratarProgressoJogo();

               tela.aguardar(20);

           }

           return _jogo.getJogador();
    }

    /**
     * Método que inicializa o jogo, criando e desenhando os elementos de jogo na área
     * de jogo.
     */
    private void inicializarJogo()
    {
          ArrayList listaPosicaoMeteoros = new ArrayList();

          // Desenhar fundo
/*          _jogoGUI.DesenharFundo(
                                    new Double(_jogo.getSatelite().getCentroY() ).intValue(),
                                    new Double(_jogo.getSatelite().getCentroX() ).intValue() );
*/

        // Desenha o Escore
          _jogoGUI.desenharEscore(
                                   _jogo.getPlaneta().getLocalizacaoAtual().getCoordenadaX(),
                                   25,
                                   _jogo.getEscore() );

        // Desenha o Fase
          _jogoGUI.desenharFase( 25, _jogo.getFase() );


        // Desenha o Chances
          _jogoGUI.desenharChances(
                                   _jogo.getPlaneta().getLocalizacaoAtual().getCoordenadaY(),
                                   25,
                                   _jogo.getChancesJogo() );

        // Desenha o planeta
          _jogoGUI.desenharPlaneta(
                                   _jogo.getPlaneta().getLocalizacaoAtual(),
                                   _jogo.getPlaneta().getDiametro() );

           // Desenha o satélite
           _jogoGUI.desenharSatelite(
                                   _jogo.getSatelite().getLocalizacaoAtual(),
                                   _jogo.getSatelite().getDiametro() );

           // Desenha os meteoros
           for(int i = 0; i < _jogo.getListaMeteoros().getQuantidadeMeteoros(); i++)
           {
              _jogoGUI.desenharMeteoro( _jogo.getListaMeteoros()
                                                                   .retornaMeteoro(i)
                                                                   .getLocalizacaoAtual(),
                                                            _jogo.getListaMeteoros()
                                                                   .retornaMeteoro(i)
                                                                   .getDiametro() );
            }


    } // fim inicializarJogo

    /**
     * Método que move os meteoros atualizando suas posições.
     */
    private void moveMeteoros()
    {
           ListaMeteoros listaMeteoros = _jogo.getListaMeteoros();
           int[] listaChaves = listaMeteoros.retornaChaveMeteoros();

           MeteoroDTO meteoro = null;
           int chave = 0;

           for(int i = 0; i < listaMeteoros.getQuantidadeMeteoros(); i++)
           {
               chave = listaChaves[i] ;

               // Realiza a atualização da posição do meteoro
                meteoro = (MeteoroDTO) new ElementoJogo().mover(
                                                               listaMeteoros.retornaMeteoro(chave) );

               /* Verifica se o missel alcançou os limites da área de jogo, caso tenha
                 * alcançado, remove o míssei da lista e apaga da tela. Caso não
                 * atualiza a imagem e os dados do missil na lista.
                 */
                tratarColisaoMeteoroTela( meteoro );

              // Realiza a atualização da imagem do meteoro
               _jogoGUI.moverMeteoro( chave, meteoro.getLocalizacaoAtual() );

              // Atualiza o meteoro na lista de meteoros
              listaMeteoros.remover(chave);
              listaMeteoros.setMeteoro(meteoro);
           }

    } // Fim moverMeteoros


    /**
     * Método que trata a colisão do meteoro com os limites da tela. set como parametro o
     * meteoro que colidio
     *
     * @param  ElementoDTO - meteoto : Meteoro que colidiu com os limites da tela.
     */
    private void tratarColisaoMeteoroTela(ElementoDTO meteoro)
    {
          double pX = meteoro.getLocalizacaoAtual().getCoordenadaX();
          double pY = meteoro.getLocalizacaoAtual().getCoordenadaY();

          int raio = meteoro.getDiametro() / 2;
          int constante = 2;

           if(  pX - raio <=  0 || pY -raio <=  0 ||
                pX + raio  >=  ( _jogo.getSatelite().getCentroX() * 2 ) ||
                pY + raio  >=  (_jogo.getSatelite().getCentroY() * 2) )
            {
                     ElementoDTO elementoTela = new ElementoDTO();

                     elementoTela.setPxVelocidade(0);
                     elementoTela.setPyVelocidade(0);

                     elementoTela.setDiametro( meteoro.getDiametro() );
                     elementoTela.setMassa( meteoro.getMassa() );

                    if(pX - raio <=  0)
                    {
                            pX =  raio;
                            elementoTela.setPxVelocidade( meteoro.getVelocidade() * constante);
                     }

                     if(pY - raio <=  0)
                     {
                            pY =  pY - raio;
                            elementoTela.setPyVelocidade( meteoro.getVelocidade() * constante);
                     }

                     if(pX + raio >=  ( _jogo.getSatelite().getCentroX() * 2 ) )
                     {
                            pX =  pX + raio;
                            elementoTela.setPxVelocidade( - (meteoro.getVelocidade() * constante) );
                     }

                     if(pY+ raio >=  ( _jogo.getSatelite().getCentroY() * 2 ) )
                     {
                            pY =  pY + raio;
                            elementoTela.setPyVelocidade( - (meteoro.getVelocidade() * constante) );
                     }

                     elementoTela.setLocalizacaoAtual( new Ponto(pX, pY) );

                     double[] novasCoordenadas = new ElementoJogo().
                                                                      redirecionarElementos(meteoro, elementoTela);

                     meteoro.setPxVelocidade( novasCoordenadas[0] );
                     meteoro.setPyVelocidade( novasCoordenadas[1] );
               }
    }

    /**
     * Método que move o satelite, conforme os comandos do teclado.
     *
     * @param  boolean - botaoDireito : Identificador de acionamento do botão direito
     * @param  boolean - botaoEsquerdo : Identificador de acionamento do botão esquerdo
     */
    private void moveSatelite(boolean botaoDireito, boolean botaoEsquerdo)
    {
            SateliteDTO satelite = _jogo.getSatelite();
            int direcao = 0;
            double angulo = - (satelite.getAngulo() - 90 );
            int suavidade = 5;

            if(botaoDireito == true)
            {
                direcao = 1;
                angulo = angulo + suavidade;
            }

            if(botaoEsquerdo == true)
            {
                direcao = 2;
                angulo = angulo - suavidade;
            }

             // Cso tenha sido dada alguma direção, realiza o movimento circular do satélite, o
             // redesenha e atualiza suas informações.
             if (direcao != 0)
             {
                    satelite = new ElementoJogo().moverCircular(satelite, direcao);
                    _jogoGUI.moverSatelite(satelite.getLocalizacaoAtual(), angulo );
             }

    } // Fim moverSatelite

    /**
     * Método que gera um novo missel e o desenha na área de jogo
     *
     * @param  boolean - botaoEspaco : Identificador de acionamento do botão espaço
     * @param  boolean - botaoAbaixo : Identificador de acionamento do botão seta para baixo
     */
    private void gerarMisseis(boolean botaoEspaco, boolean botaoAbaixo)
    {
            if(botaoEspaco == true || botaoAbaixo == true)
            {
                ArrayList listaMisseis = _jogo.getListaMisseis();

                // Verifica o limite de mísseis da tela (4 mísseis por vez)
                if( listaMisseis.size() < 4)
                {
                        ElementoDTO missil = new ElementoDTO();

                        // set as informações de diametro, velocidade e massa do míssil
                        missil.setDiametro( _jogo.getMissil().getDiametro() );
                        missil.setVelocidade( _jogo.getMissil().getVelocidade() );
                        missil.setMassa( _jogo.getMissil().getMassa() );

                        /* Informa como ponto de origem a localização atual o centro da tela e
                         * destino a posição atual do satélite de modo a gerar um vetor de direção
                         */
                        missil.setLocalizacaoAtual(  new Ponto (
                                                                    _jogo.getSatelite().getCentroX(),
                                                                    _jogo.getSatelite().getCentroY() ) );

                        missil.setDestino( new Ponto (
                                                                    _jogo.getSatelite().getLocalizacaoAtual()
                                                                    .getCoordenadaX(),
                                                                    _jogo.getSatelite().getLocalizacaoAtual()
                                                                    .getCoordenadaY() ) );

                        // Desenha o míssil
                        _jogoGUI.desenharMissil( missil.getDestino(),
                                                                  missil.getDiametro() );

                        // Determinar  os componentes de velocidade do míssil
                        missil = new ElementoJogo().determinarMovimentoInicial(missil);

                        listaMisseis.add( missil );
                        _jogo.setListaMisseis( listaMisseis );

                  } // fim if

            }// fim if

    } // Fim moverSatelite

    /**
     * Método que move os misseis atualizando suas posições.
     */
    private void moveMisseis()
    {
           ElementoDTO missil = null;

           ArrayList listaMisseis = _jogo.getListaMisseis();

           // verifica se há misseis para serem movidos
           if( !listaMisseis.isEmpty() )
           {
                 for(int i = 0; i < listaMisseis.size(); i++ )
                 {
                        // Realiza a atualização das posições dos mísseis
                        missil = (ElementoDTO) new ElementoJogo().mover(
                                                                    (ElementoDTO) listaMisseis.get(i) );

                       // Redesenha o míssel na tela
                       _jogoGUI.moverMissil( i, missil.getLocalizacaoAtual() );


                        /*Verifica se o missel alcançou os limites da área de jogo, caso tenha
                         * alcançado, remove o míssei da lista e apaga da tela. Caso não
                         * atualiza a imagem e os dados do missil na lista.
                         */
                        if(  missil.getLocalizacaoAtual().getCoordenadaX() <=  0 ||
                             missil.getLocalizacaoAtual().getCoordenadaY() <=  0 ||
                             missil.getLocalizacaoAtual().getCoordenadaX()  >=
                                                   ( _jogo.getSatelite().getCentroX() * 2 ) ||
                             missil.getLocalizacaoAtual().getCoordenadaY()  >=
                                                    (_jogo.getSatelite().getCentroY() * 2) )
                        {
                            _jogoGUI.removerMissil(i);
                            listaMisseis.remove(i);
                        }

                        else
                        {
                            _jogoGUI.moverMissil( i, missil.getLocalizacaoAtual() );
                            listaMisseis.set(i, missil);
                        }

                  } // fim for

                  _jogo.setListaMisseis(listaMisseis);

          }

          else
                 _jogoGUI.limpaListaMisseis();

    } // Fim MoverMísseis


    //******************* Métodos para a Detecção e Tratamento de Colisões  **********************
    /**
     * Método que realiza a detecção de colisão entre todos os elementos presentes na área
     * de jogo.
     */
    private void detectarColisoes()
    {
        // Caso hajam misseis verifica e trata colisão entre meteoros e mísseis
        if( ! _jogo.getListaMisseis().isEmpty() )
        {
            detectarColisaoMeteorosMissies();
        }

        detectarColisaoMeteoros();
        detectarColisaoMeteorosPlaneta();
    }


    /**
     * Método que identifica se houve colisão entre meteoros e mísseis. Caso tenha ocorrido
     * colisão, remove o míssel da área de jogo, reduz a resistência do meteoro e o redireciona.
     * Se a resistência do meteoro chegar a zero, o remove do jogo.
     */
    private void detectarColisaoMeteorosMissies()
    {
        ListaMeteoros listaMeteoros = _jogo.getListaMeteoros();
        int[] chaveMeteoros = listaMeteoros.retornaChaveMeteoros();

        MeteoroDTO meteoro = null;
        ElementoDTO missil = null;

        double[] novasCoordenadas = null;

        ArrayList listaMisseis = _jogo.getListaMisseis();

        for(int i = 0; i < listaMeteoros.getQuantidadeMeteoros(); i++ )
        {
              meteoro = listaMeteoros.retornaMeteoro( chaveMeteoros[i] );

              for(int j = 0; j < listaMisseis.size(); j++ )
              {
                   missil = (ElementoDTO) listaMisseis.get( j );

                   // Verifica se houve colisão entre cada missil e cada meteoro.
                   if( new ElementoJogo().detectarColisaoElementos( meteoro, missil  ) )
                    {
                            //Obter novas coordenadas de meteoro
                            novasCoordenadas = new ElementoJogo().
                                                               redirecionarElementos(meteoro, missil);

                           // Remove míssil do jogo
                            _jogoGUI.removerMissil( j );
                            listaMisseis.remove( j );

                            // Redireciona meteoro
                           _jogo.getListaMeteoros().retornaMeteoro(meteoro.getChave() ).setPxVelocidade(novasCoordenadas[0] );

                           _jogo.getListaMeteoros().retornaMeteoro(meteoro.getChave() ).setPyVelocidade(novasCoordenadas[1] );

                            // Trata a colisão com o meteoro
                            tratarColisaoMeteoro(meteoro);

                            // Incrementar Escore
                            _jogo.setEscore(_jogo.getEscore() + 1);
                            _jogoGUI.atualizarEscore( _jogo.getEscore() );

                            // Incrementar Chances
                            if( _jogo.getEscore() % 80 == 0)
                            {
                                _jogo.setChances(_jogo.getChancesJogo() + 1);
                                _jogoGUI.atualizarChances( _jogo.getChancesJogo() );
                            }

                    }

              } // fim while

        } // fim for

    } // fim método

    /**
     * Método que identifica se houve colisão entre meteoros. Caso tenha ocorrido colisão, reduz
     * a resistência dos meteoros e os redireciona. Se a resistência de um dos  meteoros chegar
     * a zero, o remove do jogo.
     */
    private void detectarColisaoMeteoros()
    {
        ListaMeteoros listaMeteoros = _jogo.getListaMeteoros();
        int[] chaveMeteoros = listaMeteoros.retornaChaveMeteoros();

        MeteoroDTO meteoroA = null;
        MeteoroDTO meteoroB = null;

        for(int i = 0; i < listaMeteoros.getQuantidadeMeteoros(); i++ )
        {
              meteoroA = listaMeteoros.retornaMeteoro( chaveMeteoros[i] );

              for(int j = 0; j < listaMeteoros.getQuantidadeMeteoros(); j++ )
              {
                   if (i != j)
                   {
                       meteoroB = listaMeteoros.retornaMeteoro( chaveMeteoros[j] );

                        // Verifica se houve colisão entre cada missil e cada meteoro.
                        if( new ElementoJogo().detectarColisaoElementos( meteoroA, meteoroB  ) )
                        {
                            //Obter novas coordenadas de meteoro
                            double[]  novasCoordenadas = new ElementoJogo().
                                                               redirecionarElementos(meteoroA, meteoroB);

                            // Redirecionar meteoros
                           _jogo.getListaMeteoros().
                                          retornaMeteoro(meteoroA.getChave() ).
                                                     setPxVelocidade(novasCoordenadas[0] );

                           _jogo.getListaMeteoros().
                                          retornaMeteoro(meteoroA.getChave() ).
                                                     setPyVelocidade(novasCoordenadas[1] );

                           _jogo.getListaMeteoros().
                                          retornaMeteoro(meteoroB.getChave() ).
                                                     setPxVelocidade(novasCoordenadas[2] );

                           _jogo.getListaMeteoros().
                                          retornaMeteoro(meteoroB.getChave() ).
                                                     setPyVelocidade(novasCoordenadas[3] );

                           while( new ElementoJogo().detectarColisaoElementos( meteoroA, meteoroB  ) )
                           {
                                moveMeteoros();
                            }

                           // Trata a colisão com o meteoro
                           tratarColisaoMeteoro(meteoroA);
                           tratarColisaoMeteoro(meteoroB);

                           chaveMeteoros = listaMeteoros.retornaChaveMeteoros();

                        } // fim if

                    } // fim if

              } // fim while

        } // fim for

    } // fim método


    /**
     * Método que efetua o tratamento de um elemento Meteoro após ele ter colidido com um míssil
     * ou outro meteoro.
     *
     * @param  MeteoroDTO - meteoro : Meteoro que será atualizado.
     */
    private void tratarColisaoMeteoro(MeteoroDTO meteoro)
    {
        int resistencia = meteoro.getPontosResistencia() - 1;

        if( resistencia > 0)
        {
            // Atualiza os pontoe s de resistencia
            _jogo.getListaMeteoros().retornaMeteoro(meteoro.getChave() ).
                                                                  setPontosResistencia(resistencia);
        }

        else
        {
                 // Remove o meteoro do jogo caso os pontos de resistencia tenham chegado a zero.
                 // Verifica se o meteoro ainda existe, caso tenha ocorrido o choque de três ou mais meteoros
                if( _jogo.getListaMeteoros().informaMeteoroExiste( meteoro.getChave() ) )
                {
                    _jogoGUI.removerMeteoro( meteoro.getChave() );
                    _jogo.getListaMeteoros().remover( meteoro.getChave() );

                    // Incrementar Escore
                    _jogo.setEscore(_jogo.getEscore() + 4);
                    _jogoGUI.atualizarEscore( _jogo.getEscore() );

                }
          }
    }

    /**
     * Método que identifica se houve colisão entre meteoros e o Planeta. Caso tenha ocorrido
     * colisão, reinicia a movimentação dos meteoros e reduz as chances de jogo.
     */
    private void detectarColisaoMeteorosPlaneta()
    {
        ListaMeteoros listaMeteoros = _jogo.getListaMeteoros();
        int[] chaveMeteoros = listaMeteoros.retornaChaveMeteoros();

        boolean colisao = false;

        MeteoroDTO meteoro = null;
        ElementoDTO planeta = _jogo.getPlaneta();;


        for(int i = 0; i < listaMeteoros.getQuantidadeMeteoros(); i++ )
        {
              meteoro = listaMeteoros.retornaMeteoro( chaveMeteoros[i] );

              // Verifica se houve colisão entre cada missil e cada meteoro.
              if( new ElementoJogo().detectarColisaoElementos( meteoro, planeta  ) )
               {
                    colisao = true;
                }

         }

         if(colisao)
         {
                // Apagar os meteoros atuais
                for(int j = 0; j < _jogo.getListaMeteoros().getQuantidadeMeteoros(); j++)
                {
                    _jogoGUI.removerMeteoro( chaveMeteoros[j] );
                 }

                 _jogoGUI.limpaListaMeteoro();


                // Obtem resitencias atuais
                int[] listaResistencias = new int[ listaMeteoros.getQuantidadeMeteoros() ];

                for(int k = 0; k < listaMeteoros.getQuantidadeMeteoros(); k++ )
                {
                      meteoro = listaMeteoros.retornaMeteoro( chaveMeteoros[k] );
                      listaResistencias[k] = meteoro.getPontosResistencia();
                }

                // Gera nova lista de meteoros na posição inicial
                listaMeteoros = new GeradorJogo().gerarMeteoros(
                                                 listaMeteoros.getQuantidadeMeteoros(), listaResistencias);

                _jogo.setListaMeteoros(listaMeteoros);

                // Desenha os meteoros
                for(int l = 0; l < _jogo.getListaMeteoros().getQuantidadeMeteoros(); l++)
                {
                    _jogoGUI.desenharMeteoro( _jogo.getListaMeteoros()
                                                                    .retornaMeteoro(l)
                                                                    .getLocalizacaoAtual(),
                                                             _jogo.getListaMeteoros()
                                                                    .retornaMeteoro(l)
                                                                    .getDiametro() );
                 }

                // Decrementa chances
                _jogo.setChances(_jogo.getChancesJogo() - 1);
                _jogoGUI.atualizarChances( _jogo.getChancesJogo() );

         }
    }

    /**
     *  Método que efetua a evolução das fases de jogo após os meteoros da fase atual terem sido
     *  destruidos.
     *
     */
    private void tratarProgressoJogo()
    {
        if( _jogo.getListaMeteoros().getQuantidadeMeteoros() <= 0)
        {
                // Incrementar Fase
                _jogo.setFase(_jogo.getFase() + 1);
                _jogoGUI.atualizarFase( _jogo.getFase() );
                _jogo.setNivelPartida(_jogo.getFase());
                // Apagar os meteoros atuais
                 _jogoGUI.limpaListaMeteoro();

                // Obtem a quantidade atual dos meteoros
                int quantidadeMeteoros = (new Float( (_jogo.getFase() - 1) / 4).intValue() ) + 3;

                // Obtem a resistencia atual dos meteoros
                int resistenciaMeteoros = ( (_jogo.getFase() - 1)  -
                                              ( (new Float( (_jogo.getFase() - 1) / 4).intValue() ) * 4 ) ) +1 ;

                // Obtem resitencias atuais
                int[] listaResistencias = new int[ quantidadeMeteoros ];

                for(int k = 0; k < quantidadeMeteoros; k++ )
                {
                      listaResistencias[k] = resistenciaMeteoros;
                }

                // Gera nova lista de meteoros na posição inicial
                ListaMeteoros listaMeteoros = new GeradorJogo().gerarMeteoros(
                                                 quantidadeMeteoros, listaResistencias);

                _jogo.setListaMeteoros(listaMeteoros);

                // Desenha os meteoros
                for(int l = 0; l < _jogo.getListaMeteoros().getQuantidadeMeteoros(); l++)
                {
                    _jogoGUI.desenharMeteoro( _jogo.getListaMeteoros()
                                                                    .retornaMeteoro(l)
                                                                    .getLocalizacaoAtual(),
                                                             _jogo.getListaMeteoros()
                                                                    .retornaMeteoro(l)
                                                                    .getDiametro() );
                 } // fim for

        } // fim if

    } // fim tratarProgressoJogo

}
